package com.ideaaedi.springcloud.jd.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.ideaaedi.springcloud.jd.commonds.constant.BaseConstant;
import com.ideaaedi.springcloud.jd.commonds.entity.PageInfo;
import com.ideaaedi.springcloud.jd.commonds.entity.user.bo.DataScopeInfoBO;
import com.ideaaedi.springcloud.jd.commonds.enums.user.DataScopeTypeEnum;
import com.ideaaedi.springcloud.jd.commonds.entity.user.po.SysDataScopePO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.po.SysDeptPathTracePO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.po.SysUserPO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.req.SysApiResourceListReqVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.req.SysDataScopeListReqVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.req.SysDataScopeUpdateReqVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.resp.SysApiResourceListRespVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.resp.SysDataScopeDetailRespVO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.vo.resp.SysDataScopeListRespVO;
import com.ideaaedi.springcloud.jd.commonspring.mybatisplus.ServiceImplExt;
import com.ideaaedi.springcloud.jd.commonspring.validator.SysValidator;
import com.ideaaedi.springcloud.jd.user.mapper.SysDataScopeMapper;
import com.ideaaedi.springcloud.jd.user.service.SysApiResourceService;
import com.ideaaedi.springcloud.jd.user.service.SysDataScopeService;
import com.ideaaedi.springcloud.jd.user.service.SysDeptPathTraceService;
import com.ideaaedi.springcloud.jd.user.service.SysTenantService;
import com.ideaaedi.springcloud.jd.user.service.SysUserService;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 系统-数据操作范围 服务实现类
 * </p>
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 2021.0.5.B
 */
@Service
public class SysDataScopeServiceImpl extends ServiceImplExt<SysDataScopeMapper, SysDataScopePO> implements SysDataScopeService {
    
    @Resource
    private SysTenantService sysTenantService;
    
    @Resource
    private SysUserService sysUserService;
    
    @Resource
    private SysApiResourceService sysApiResourceService;
    
    @Resource
    private SysDeptPathTraceService sysDeptPathTraceService;
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysDataScopeDetailRespVO update(SysDataScopeUpdateReqVO req) {
        Long userId = req.getUserId();
        List<Integer> readDeptIdList = req.getReadDeptIdList();
        List<Integer> updateDeptIdList = req.getUpdateDeptIdList();
        String remark = req.getRemark();
        List<Long> apiIds = req.getApiIds();
        Integer deptHisVisible = req.getDeptHisVisible() == null ? 1 : req.getDeptHisVisible();
        SysValidator.verifyCouldDelThisId(userId);
        
        // 先移除旧的
        remove(new LambdaQueryWrapper<SysDataScopePO>()
                .eq(SysDataScopePO::getUserId, userId)
        );
        
        // 添加新的可读范围
        if (!CollectionUtils.isEmpty(readDeptIdList)) {
            List<SysDataScopePO> poList = readDeptIdList.stream().map(x -> {
                SysDataScopePO po = new SysDataScopePO();
                po.setId(IdWorker.getId());
                po.setType(DataScopeTypeEnum.READ);
                po.setUserId(userId);
                po.setDeptId(x);
                po.setDeptHisVisible(deptHisVisible);
                po.setApiIds(apiIds == null ? Collections.emptyList() : apiIds);
                po.setRemark(remark);
                return po;
            }).collect(Collectors.toList());
            saveBatch(poList);
        }
        
        // 添加新的可写范围
        if (!CollectionUtils.isEmpty(updateDeptIdList)) {
            List<SysDataScopePO> poList = updateDeptIdList.stream().map(x -> {
                SysDataScopePO po = new SysDataScopePO();
                po.setId(IdWorker.getId());
                po.setType(DataScopeTypeEnum.UPDATE);
                po.setUserId(userId);
                po.setDeptId(x);
                po.setDeptHisVisible(deptHisVisible);
                po.setApiIds(apiIds == null ? Collections.emptyList() : apiIds);
                po.setRemark(remark);
                return po;
            }).collect(Collectors.toList());
            saveBatch(poList);
        }
    
        return detailByUserId(userId);
    }
    
    @Override
    public SysDataScopeDetailRespVO detailByUserId(Long userId) {
        SysDataScopeListReqVO req = new SysDataScopeListReqVO();
        req.setPageSize(BaseConstant.IGNORE_PAGING);
        req.setUserIdList(Lists.newArrayList(userId));
        List<SysDataScopeListRespVO> dataList = list(req).getDataList();
        if (CollectionUtils.isEmpty(dataList)) {
            SysDataScopeDetailRespVO resp = new SysDataScopeDetailRespVO();
            resp.setUserId(userId);
            SysUserPO user = sysUserService.getById(userId);
            resp.setUsername(user == null ? null : user.getName());
            return resp;
        }
    
        // 查询历史可见的dept_path
        Map<Integer, Set<String>> deptIdAndHisPathSetMap = obtainDeptIdAndHisPathSetMap(dataList);
    
        // 查询api信息
        Set<Long> apiIdsSet = dataList.stream()
                .filter(Objects::nonNull)
                .flatMap(x -> {
                    List<Long> apiIds = x.getApiIds();
                    if (CollectionUtils.isEmpty(apiIds)) {
                        return Stream.empty();
                    }
                    return apiIds.stream();
                }).collect(Collectors.toSet());
    
        Map<Long, SysApiResourceListRespVO> apiIdAndIdentityMap;
        if (CollectionUtils.isEmpty(apiIdsSet)) {
            apiIdAndIdentityMap = Collections.emptyMap();
        } else {
            SysApiResourceListReqVO sysApiResourceListReq = new SysApiResourceListReqVO();
            sysApiResourceListReq.setIdSet(apiIdsSet);
            sysApiResourceListReq.setPageSize(BaseConstant.IGNORE_PAGING);
            List<SysApiResourceListRespVO> tmpList = sysApiResourceService.list(sysApiResourceListReq).getDataList();
            apiIdAndIdentityMap = tmpList.stream().collect(Collectors.toMap(SysApiResourceListRespVO::getId, Function.identity()));
        }
        
        // 组装数据并返回
        SysDataScopeDetailRespVO resp = new SysDataScopeDetailRespVO();
        List<DataScopeInfoBO> readDataScopeList = new ArrayList<>(8);
        List<DataScopeInfoBO> updateDataScopeList = new ArrayList<>(8);
        SysDataScopeListRespVO sysDataScopeListResp;
        DataScopeTypeEnum dataScopeTypeEnum;
        DataScopeInfoBO bo;
        for (int i = 0; i < dataList.size(); i++) {
            sysDataScopeListResp = dataList.get(i);
            if (i == 0) {
                resp.setUserId(sysDataScopeListResp.getUserId());
                resp.setUsername(sysDataScopeListResp.getUsername());
            }
            dataScopeTypeEnum = sysDataScopeListResp.getType();
            bo = new DataScopeInfoBO();
            BeanUtils.copyProperties(sysDataScopeListResp, bo);
            List<Long> apiIds = sysDataScopeListResp.getApiIds();
            if (!CollectionUtils.isEmpty(apiIds)) {
                bo.setApiList(apiIds.stream().map(apiIdAndIdentityMap::get).collect(Collectors.toList()));
            }
            bo.setDeptHisPathSet(deptIdAndHisPathSetMap.get(bo.getDeptId()));
            switch (dataScopeTypeEnum) {
                case READ:
                    readDataScopeList.add(bo);
                    break;
                case UPDATE:
                    updateDataScopeList.add(bo);
                    break;
                default:
                    throw new UnsupportedOperationException("un-support dataScopeTypeEnum '" + dataScopeTypeEnum + "'");
            }
        }
        resp.setReadDataScopeList(readDataScopeList);
        resp.setUpdateDataScopeList(updateDataScopeList);
        return resp;
    }
    
    @Override
    public PageInfo<SysDataScopeListRespVO> list(SysDataScopeListReqVO req) {
        int pageNum = req.getPageNum();
        int pageSize = req.getPageSize();
        
        // 分页查
        IPage<SysDataScopePO> pageInfo = new Page<>(pageNum, pageSize);
        IPage<SysDataScopeListRespVO> page = this.baseMapper.list(pageInfo, req);
        List<SysDataScopeListRespVO> records = page.getRecords();
        
        // 查询并填充租户信息
        Set<String> tenantSet = records.stream()
                .filter(Objects::nonNull)
                .map(SysDataScopeListRespVO::getTenant).collect(Collectors.toSet());
        Map<String, String> tenantAndNameMap = sysTenantService.mappingTenantName(tenantSet, true);
        records.forEach(resp -> {
            resp.setTenantName(tenantAndNameMap.get(resp.getTenant()));
        });
        return PageInfo.of(page.getTotal(), pageNum, pageSize, records);
    }
    
    /**
     * 获取部门id-部门历史路径 map
     *
     * @param dataList 包含部门id的数据集合
     *
     * @return 部门id-部门历史路径 map
     */
    @NotNull
    private Map<Integer, Set<String>> obtainDeptIdAndHisPathSetMap(@NotNull List<SysDataScopeListRespVO> dataList) {
        Set<Integer> deptHisVisibleDeptIdSet = dataList.stream()
                .filter(x -> x != null && Objects.equals(x.getDeptHisVisible(), 1))
                .map(SysDataScopeListRespVO::getDeptId).collect(Collectors.toSet());
        Map<Integer, Set<String>> deptIdAndPathsMap = new HashMap<>();
        if (CollectionUtils.isEmpty(deptHisVisibleDeptIdSet)) {
            return deptIdAndPathsMap;
        }
        List<SysDeptPathTracePO> list = sysDeptPathTraceService.list(new LambdaQueryWrapper<SysDeptPathTracePO>()
                .select(SysDeptPathTracePO::getDeptId, SysDeptPathTracePO::getDeptPath)
                .in(SysDeptPathTracePO::getDeptId, deptHisVisibleDeptIdSet)
        );
        for (SysDeptPathTracePO sysDeptPathTrace : list) {
            Integer deptId = sysDeptPathTrace.getDeptId();
            Set<String> deptPathSet = deptIdAndPathsMap.get(deptId);
            if (deptPathSet == null) {
                deptIdAndPathsMap.put(deptId, new HashSet<>());
                deptPathSet = deptIdAndPathsMap.get(deptId);
            }
            deptPathSet.add(sysDeptPathTrace.getDeptPath());
        }
        return deptIdAndPathsMap;
    }
}
